package com.young.common.util;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.sql.Timestamp;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * 日期处理工具类
 */
public class DateUtil {

    //public static final String defaultDatePattern = "yyyy-MM-dd HH:mm:ss.SSS";

    private static final Logger logger = LoggerFactory.getLogger(DateUtil.class);


    /**
     * 时间格式化模式枚举类
     */
    public enum FormatPattern {
        DEFAULT("yyyy-MM-dd HH:mm:ss"),
        DEFAULT_DATE("yyyy-MM-dd"),
        DEFAULT_TIME("yyyy-MM-dd HH:mm:ss"),

        YYYY_MM_DD_HH_MI_SS_SSS("yyyy-MM-dd HH:mm:ss.SSS"),
        YYYY_MM_DD_HH_MI_SS("yyyy-MM-dd HH:mm:ss"),
        YYYY_MM_DD_HH_MI("yyyy-MM-dd HH:mm"),
        YYYY_MM_DD_HH("yyyy-MM-dd HH"),
        YYYY_MM_DD("yyyy-MM-dd"),
        YYYY_MM("yyyy-MM"),

        S_YYYY_MM_DD_HH_MI_SS_SSS("yyyy/MM/dd HH:mm:ss.SSS"),
        S_YYYY_MM_DD_HH_MI_SS("yyyy/MM/dd HH:mm:ss"),
        S_YYYY_MM_DD_HH_MI("yyyy/MM/dd HH:mm"),
        S_YYYY_MM_DD_HH("yyyy/MM/dd HH"),
        S_YYYY_MM_DD("yyyy/MM/dd"),
        S_YYYY_MM("yyyy/MM"),

        CN_YYYY_MM_DD_HH_MI_SS("yyyy年MM月dd日 HH时mm分ss秒"),
        CN_YYYY_MM_DD_HH_MI("yyyy年MM月dd日 HH时mm分"),
        CN_YYYY_MM_DD_HH("yyyy年MM月dd日 HH时"),
        CN_YYYY_MM_DD$HH_MI_SS("yyyy年MM月dd日HH时mm分ss秒"),
        CN_YYYY_MM_DD$HH_MI("yyyy年MM月dd日HH时mm分"),
        CN_YYYY_MM_DD$HH("yyyy年MM月dd日HH时"),
        CN_YYYY_MM_DD("yyyy年MM月dd日"),
        CN_YYYY_MM("yyyy年MM月"),

        N_YYYY$MM$DD$HH$MI$SS_SSS("yyyyMMddHHmmss.SSS"),
        N_YYYY$MM$DD$HH$MI$SS$SSS("yyyyMMddHHmmssSSS"),
        N_YYYY$MM$DD$HH$MI$SS("yyyyMMddHHmmss"),
        N_YYYY$MM$DD$HH$MI("yyyyMMddHHmm"),
        N_YYYY$MM$DD$HH("yyyyMMddHH"),
        N_YYYY$MM$DD("yyyyMMdd"),
        N_YYYY$MM("yyyyMM");
        private String pattern;//格式化模式

        FormatPattern(String pattern) {
            this.pattern = pattern;
        }

        public String getPattern() {
            return pattern;
        }

        public SimpleDateFormat getDateFormat() {
            return new SimpleDateFormat(pattern);
        }
    }

    /**
     * 获取时间格式化模式
     *
     * @param dateStr
     * @return
     */
    public static FormatPattern getFormatPattern(String dateStr) {
        if (dateStr == null) {
            return null;
        }
        FormatPattern result = null;
        if (dateStr.matches("\\d{4}-\\d{1,2}-\\d{1,2} \\d{1,2}:\\d{1,2}:\\d{1,2}.\\d+")) {
            result = FormatPattern.YYYY_MM_DD_HH_MI_SS_SSS;
        } else if (dateStr.matches("\\d{4}-\\d{1,2}-\\d{1,2} \\d{1,2}:\\d{1,2}:\\d{1,2}")) {
            result = FormatPattern.YYYY_MM_DD_HH_MI_SS;
        } else if (dateStr.matches("\\d{4}-\\d{1,2}-\\d{1,2} \\d{1,2}:\\d{1,2}")) {
            result = FormatPattern.YYYY_MM_DD_HH_MI;
        } else if (dateStr.matches("\\d{4}-\\d{1,2}-\\d{1,2} \\d{1,2}")) {
            result = FormatPattern.YYYY_MM_DD_HH;
        } else if (dateStr.matches("\\d{4}-\\d{1,2}-\\d{1,2}")) {
            result = FormatPattern.YYYY_MM_DD;
        } else if (dateStr.matches("\\d{4}-\\d{1,2}")) {
            result = FormatPattern.YYYY_MM;
        } else if (dateStr.matches("\\d{4}/\\d{1,2}/\\d{1,2} \\d{1,2}:\\d{1,2}:\\d{1,2}.\\d+")) {
            result = FormatPattern.S_YYYY_MM_DD_HH_MI_SS_SSS;
        } else if (dateStr.matches("\\d{4}/\\d{1,2}/\\d{1,2} \\d{1,2}:\\d{1,2}:\\d{1,2}")) {
            result = FormatPattern.S_YYYY_MM_DD_HH_MI_SS;
        } else if (dateStr.matches("\\d{4}/\\d{1,2}/\\d{1,2} \\d{1,2}:\\d{1,2}")) {
            result = FormatPattern.S_YYYY_MM_DD_HH_MI;
        } else if (dateStr.matches("\\d{4}/\\d{1,2}/\\d{1,2} \\d{1,2}")) {
            result = FormatPattern.S_YYYY_MM_DD_HH;
        } else if (dateStr.matches("\\d{4}/\\d{1,2}/\\d{1,2}")) {
            result = FormatPattern.S_YYYY_MM_DD;
        } else if (dateStr.matches("\\d{4}/\\d{1,2}")) {
            result = FormatPattern.S_YYYY_MM;
        } else if (dateStr.matches("\\d{4}年\\d{1,2}月\\d{1,2}日\\d{1,2}时\\d{1,2}分\\d{1,2}秒")) {
            result = FormatPattern.CN_YYYY_MM_DD$HH_MI_SS;
        } else if (dateStr.matches("\\d{4}年\\d{1,2}月\\d{1,2}日\\d{1,2}时\\d{1,2}分")) {
            result = FormatPattern.CN_YYYY_MM_DD$HH_MI;
        } else if (dateStr.matches("\\d{4}年\\d{1,2}月\\d{1,2}日\\d{1,2}时")) {
            result = FormatPattern.CN_YYYY_MM_DD$HH;
        } else if (dateStr.matches("\\d{4}年\\d{1,2}月\\d{1,2}日")) {
            result = FormatPattern.CN_YYYY_MM_DD;
        } else if (dateStr.matches("\\d{4}年\\d{1,2}月")) {
            result = FormatPattern.CN_YYYY_MM;
        } else if (dateStr.matches("\\d{4}年\\d{1,2}月\\d{1,2}日 \\d{1,2}时\\d{1,2}分\\d{1,2}秒")) {
            result = FormatPattern.CN_YYYY_MM_DD_HH_MI_SS;
        } else if (dateStr.matches("\\d{4}年\\d{1,2}月\\d{1,2}日 \\d{1,2}时\\d{1,2}分")) {
            result = FormatPattern.CN_YYYY_MM_DD_HH_MI;
        } else if (dateStr.matches("\\d{4}年\\d{1,2}月\\d{1,2}日 \\d{1,2}时")) {
            result = FormatPattern.CN_YYYY_MM_DD_HH;
        } else if (dateStr.matches("\\d{14}.\\d+")) {
            result = FormatPattern.N_YYYY$MM$DD$HH$MI$SS_SSS;
        } else if (dateStr.matches("\\d{17}")) {
            result = FormatPattern.N_YYYY$MM$DD$HH$MI$SS$SSS;
        } else if (dateStr.matches("\\d{14}")) {
            result = FormatPattern.N_YYYY$MM$DD$HH$MI$SS;
        } else if (dateStr.matches("\\d{12}")) {
            result = FormatPattern.N_YYYY$MM$DD$HH$MI;
        } else if (dateStr.matches("\\d{10}")) {
            result = FormatPattern.N_YYYY$MM$DD$HH;
        } else if (dateStr.matches("\\d{8}")) {
            result = FormatPattern.N_YYYY$MM$DD;
        } else if (dateStr.matches("\\d{6}")) {
            result = FormatPattern.N_YYYY$MM;
        }
        return result;
    }

    /**
     * 根据时间字符串获取对应的格式化模式
     *
     * @param dateStr 时间字符串
     * @return
     */
    public static String getFormatPatternAsString(String dateStr) {
        FormatPattern formatPattern = getFormatPattern(dateStr);
        return formatPattern != null ? formatPattern.getPattern() : null;
    }

    /**
     * 根据时间字符串获取对应的时间格式化器
     *
     * @param dateStr
     * @return
     */
    private static SimpleDateFormat getFormatPatternAsSimpleDateFormat(String dateStr) {
        FormatPattern formatPattern = getFormatPattern(dateStr);
        return formatPattern != null ? formatPattern.getDateFormat() : null;
    }


    /**
     * 将日期对象格式化成字符串
     *
     * @param dateObj 日期对象 Date/Calendar
     * @return String 格式化的日期表示
     */
    public static String dateFormat(Object dateObj) {
        return dateFormat(dateObj, null);
    }

    /**
     * 将日期对象格式化
     *
     * @param dateObj 日期对象 Date/Calendar
     * @param pattern 日期和时间的表示格式
     * @return String 格式化的日期表示
     */
    public static String dateFormat(Object dateObj, String pattern) {
        SimpleDateFormat sdf = (pattern == null) ? FormatPattern.DEFAULT.getDateFormat() : new SimpleDateFormat(pattern);
        if (dateObj == null) {
            return null;
        } else if (dateObj instanceof Calendar) { // Calendar
            Date date = ((Calendar) dateObj).getTime();
            return sdf.format(date);
        } else if (dateObj instanceof java.sql.Date) {
            Date date = new Date(((java.sql.Date) dateObj).getTime());
            return sdf.format(date);
        } else if (dateObj instanceof java.sql.Time) {
            Date date = new Date(((java.sql.Time) dateObj).getTime());
            return sdf.format(date);
        } else if (dateObj instanceof Timestamp) {
            Date date = new Date(((Timestamp) dateObj).getTime());
            return sdf.format(date);
        } else if (dateObj instanceof Date) {
            return sdf.format((Date) dateObj);
        } else {
            return null;
        }
    }

    /**
     * 时间字符串转时间(自动识别格式化模式)
     *
     * @param dateStr 时间字符串
     * @return
     */
    public static Date toDate(String dateStr) {
        return toDate(dateStr, null);
    }

    /**
     * 根据输入的日期字符串构造日期对象，并返回
     *
     * @param dateStr String 日期字符串，如：2005-01-01 12:00:00.000
     * @param pattern String 日期格式
     * @return Date
     */
    public static Date toDate(String dateStr, String pattern) {
        SimpleDateFormat sdf = (pattern != null) ? new SimpleDateFormat(pattern) : getFormatPatternAsSimpleDateFormat(dateStr);
        if (sdf == null) {
            sdf = FormatPattern.DEFAULT.getDateFormat();
            logger.warn("[时间格式化工具-字符串转时间] 匹配不到对应的格式化模式,字符串={},入参pattern={},设置使用默认的时间格式化器={}", dateStr, pattern, FormatPattern.DEFAULT.getPattern());
        }
        Date date = null;
        try {
            date = sdf.parse(dateStr);
        } catch (ParseException e) {
            logger.error("[时间格式化工具-字符串转时间] 字符串={},入参pattern={},实际使用格式化器={},异常:", dateStr, pattern, sdf.toPattern(), e);
        }
        return date;
    }

    public static void main(String[] args) {
        System.out.println(diffDate(toDate("2018-12-12 12"), toDate("2018-12-13 12")));
    }

    /**
     * 计算两个日期之间相差的天数 if date1 > date2 返回正数 else if date1 < date2 返回负数 else 返回 0
     *
     * @param date1 java.util.Date
     * @param date2 java.util.Date
     * @return int
     */
    public static int diffDate(Date date1, Date date2) {
        GregorianCalendar gc1 = new GregorianCalendar();
        GregorianCalendar gc2 = new GregorianCalendar();
        gc1.setTime(date1);
        gc2.setTime(date2);
        return getDays(gc1, gc2);
    }

    /**
     * 计算两个日期之间相差的小时 if date1 > date2 返回正数 else if date1 < date2 返回负数 else 返回 0
     *
     * @param date1 java.util.Date
     * @param date2 java.util.Date
     * @return int
     */
    public static long diffHour(Date date1, Date date2) {
        long time1 = date1.getTime();
        long time2 = date2.getTime();
        return (time1 - time2) / (1000 * 3600);
    }

    /**
     * 计算相差多少天(向下取整,参数1小于参数2时返回负数)
     *
     * @param g1
     * @param g2
     * @return
     */
    public static int getDays(GregorianCalendar g1, GregorianCalendar g2) {
        int elapsed = 0;
        GregorianCalendar gc1, gc2;

        if (g2.after(g1)) {
            gc2 = (GregorianCalendar) g2.clone();
            gc1 = (GregorianCalendar) g1.clone();
        } else {
            gc2 = (GregorianCalendar) g1.clone();
            gc1 = (GregorianCalendar) g2.clone();
        }

        gc1.clear(Calendar.MILLISECOND);
        gc1.clear(Calendar.SECOND);
        gc1.clear(Calendar.MINUTE);

        gc2.clear(Calendar.MILLISECOND);
        gc2.clear(Calendar.SECOND);
        gc2.clear(Calendar.MINUTE);

        while (gc1.before(gc2)) {
            gc1.add(Calendar.HOUR_OF_DAY, 1);
            elapsed++;
        }

        return g1.after(g2) ? elapsed / 24 : -elapsed / 24;
    }

    /**
     * 相对与gc的一周的第一天
     */
    public static GregorianCalendar getFirstWeekday(GregorianCalendar gc) {
        gc.add(Calendar.DAY_OF_MONTH, -(gc.get(Calendar.DAY_OF_WEEK) - 1));
        return gc;
    }

    /**
     * 相对与gc的一月的第一天
     */
    public static GregorianCalendar getFirstMonth(GregorianCalendar gc) {
        gc.set(Calendar.DAY_OF_MONTH, 1);
        return gc;
    }

    /**
     * 相对与gc的一年的第一天
     */
    public static GregorianCalendar getFirstYear(GregorianCalendar gc) {
        gc.set(Calendar.MONTH, 0);
        gc.set(Calendar.DAY_OF_MONTH, 1);
        return gc;
    }

    /**
     * 指定日期前几天或者后几天的日期
     *
     * @param n
     * @return
     */
    public static String afterNDay(Date date, int n, String format) {
        SimpleDateFormat dateFormat = new SimpleDateFormat(format);
        String sDate = dateFormat.format(afterNDay(date, n));
        return sDate;
    }

    /**
     * 获取n天后的时间
     * @param date
     * @param n
     * @return
     */
    public static Date afterNDay(Date date, int n) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(Calendar.DATE, n);
        return calendar.getTime();
    }

    /**
     * 时间转为日期, 清空时分秒
     * @param date
     * @return
     */
    public static Date time2Date(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        return calendar.getTime();
    }

    /**
     * 获取当前日期
     *
     * @return
     */
    public static String getCurrentDay() {
        SimpleDateFormat sf = FormatPattern.DEFAULT_DATE.getDateFormat();//new SimpleDateFormat("yyyy-MM-dd");
        return sf.format(new Date());
    }

    /**
     * 获取当天的日期对象
     *
     * @return
     */
    public static Date getCurrentDayAsDate() {
        return toDate(getCurrentDay(), FormatPattern.DEFAULT_DATE.getPattern());
    }

    /**
     * 获取当前时间
     *
     * @return
     */
    public static String getCurrentTime() {
        SimpleDateFormat sf = FormatPattern.DEFAULT_TIME.getDateFormat();//new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        return sf.format(new Date());
    }

    /**
     * 获取当前年度
     *
     * @return
     */
    public static int getCurrentYear() {
        Calendar c = Calendar.getInstance();
        return c.get(Calendar.YEAR);
    }

    /**
     * 获取当前月份,比如当前是8月份,就返回8
     *
     * @return
     */
    public static int getCurrentMonth() {
        Calendar c = Calendar.getInstance();
        int month = c.get(Calendar.MONTH);
        return month + 1;
    }

    //获取当前月最后一天日期
    public static String getCurrentMonthMaxDay() {
        Calendar c = Calendar.getInstance();
        String year = getCurrentYear() + "";
        String month = getCurrentMonth() + "";
        String day = getMaxDay(year, month) + 1 + "";
        return year + "-" + month + "-" + day;
    }

    // 获取当前季度
    public static int getCurrentQuarter() {
        Calendar c = Calendar.getInstance();
        int month = c.get(Calendar.MONTH);
        month = month + 1;
        int quarter = 0;
        if (month >= 1 && month <= 3) {
            quarter = 1;
        } else if (month >= 4 && month <= 6) {
            quarter = 2;
        } else if (month >= 7 && month <= 9) {
            quarter = 3;
        } else if (month >= 10 && month <= 12) {
            quarter = 4;
        }
        return quarter;
    }

    /**
     * 获取指定年月的最大天数
     *
     * @param year  指定年
     * @param month 指定月
     * @return
     */
    public static int getMaxDay(String year, String month) {
        Calendar rightNow = Calendar.getInstance();
        rightNow.set(Calendar.YEAR, Integer.parseInt(year));
        rightNow.set(Calendar.MONTH, Integer.parseInt(month));
        int days = rightNow.getActualMaximum(Calendar.DAY_OF_MONTH);
        return days;
    }

    /**
     * 获取指定年月的周末日期
     *
     * @param year  指定年
     * @param month 指定月
     * @return
     */
    public static String getWeekEnd(String year, String month) {

        StringBuffer sb = new StringBuffer();
        int dayNum = getMaxDay(year, month);
        Calendar calendar = Calendar.getInstance(Locale.CHINA);
        calendar.set(Calendar.YEAR, Integer.parseInt(year));
        calendar.set(Calendar.MONTH, Integer.parseInt(month) - 1);
        calendar.set(Calendar.DATE, 1);

        SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd", Locale.CHINA);
        for (int i = 0; i < dayNum; i++) {
            if (calendar.get(Calendar.DAY_OF_WEEK) == 1 || calendar.get(Calendar.DAY_OF_WEEK) == 7) {
                sb.append(sf.format(calendar.getTime()) + ",");
            }
            calendar.add(Calendar.DATE, 1);
        }
        sb.deleteCharAt(sb.length() - 1);
        return sb.toString();
    }
}
